/* eslint-disable complexity */
/* eslint-disable camelcase */
/* eslint-disable wrap-regex */
const GetType = param => {
	if (typeof param === 'undefined') {
		return 'undefined';
	}
	if (param === null) {
		return 'null';
	}
	let symStrTag = Symbol.toStringTag;
	if (symStrTag && param[symStrTag]) {
		let strTag = param[symStrTag];
		let isOwn = Object.prototype.hasOwnProperty.call(param, symStrTag);
		if (!Object.getOwnPropertyDescriptor(param, symStrTag).writable) {
			return Object.prototype.toString.call(param);
		}
		param[symStrTag] = void 0;
		let result = Object.prototype.toString.call(param);
		if (isOwn) {
			param[symStrTag] = strTag;
		} else {
			delete param[symStrTag];
		}
		return result;
	}
	return Object.prototype.toString.call(param);
};

let extensionMap = {
	/* ===== List ===== */
	$__utilityList() {
		return Object.keys(extensionMap);
	},
	/* ===== Simple ===== */
	$isBoolean(param) {
		return param === true || param === false
			|| this.$isObjectLike(param) && Object.getPrototypeOf(param) === Boolean.prototype && GetType(param) === '[object Boolean]';
	},
	$isNumber(param) {
		return typeof param === 'number'
			|| this.$isObjectLike(param) && Object.getPrototypeOf(param) === Number.prototype && GetType(param) === '[object Number]';
	},
	$isInteger(param) {
		return Number.isInteger(param);
	},
	$isString(param) {
		return typeof param === 'string'
			|| this.$isObjectLike(param) && Object.getPrototypeOf(param) === String.prototype && !this.$isArray(param) && GetType(param) === '[object String]';
	},
	$isNull(param) {
		return param === null;
	},
	$isUndefined(param) {
		return typeof param === 'undefined';
	},
	$isNil(param) {
		return this.$isNull(param) || this.$isUndefined(param);
	},
	$isObject(param) {
		let type = typeof param;
		return param !== null && (type === 'object' || type === 'function');
	},
	$isObjectLike(param) {
		return param !== null && typeof param === 'object';
	},
	$isPlainObject(param) {
		let proto = Object.getPrototypeOf(Object(param));
		return this.$isObjectLike(param) && (proto === Object.prototype || proto === null) && GetType(param) === '[object Object]';
	},
	$isArray(param) {
		return Array.isArray(param);
	},
	$isArrayLike(param) {
		return param !== null && Number.isSafeInteger(param.length) && param.length >= 0 && !this.$isFunction(param);
	},
	$isArrayLikeObject(param) {
		return this.$isObjectLike(param) && this.$isArrayLike(param);
	},
	$isFunction(param) {
		return this.$isObject(param) && !!~[
			'[object Function]',
			'[object GeneratorFunction]',
			'[object AsyncFunction]',
			'[object Proxy]'
		].indexOf(GetType(param));
	},
	$isSymbol(param) {
		return typeof param === 'symbol';
	},
	/* ===== Complex ===== */
	$isCellphone(str, isStrict = false) {
		return (isStrict ? /^(1[3,5,8]\d|14[5,7]|17[0,6,7,8]|19[7])\d{8}$/ : /^1\d{10}$/).test(str);
	},
	$isLengthBetween(str, min, max) {
		return new RegExp(`^.{${min},${max}}$`).test(str);
	},
	$hasNumber(str) {
		return /\d/.test(str);
	},
	$hasUpperCase(str) {
		return /[A-Z]/.test(str);
	},
	$hasLowerCase(str) {
		return /[a-z]/.test(str);
	},
	// TODO use $isEquivalent
	$isEmpty(param) {
		return this.$isNil(param)
			|| this.$isArrayLike(param) && !param.length
			|| (GetType(param) === '[object Map]' || GetType(param) === '[object Set]') && !param.size
			|| (param === param.constructor.prototype || Object.prototype) && JSON.stringify(Object.keys(param)) === JSON.stringify(['constructor'])
			|| !Object.keys(param).length
			|| true;
	},
	$isFalsy(param) {
		let jsonStr = JSON.stringify(param);
		return !param || jsonStr === '[]' || jsonStr === '{}' || this.$isNumber(param) && Math.abs(param) < Number.EPSILON;
	}
	/*$isEquivalent(param1, param2) {
		return param1 === param2 ||
			this.$isObjectLike(param1) && this.$isObjectLike(param2) && JSON.stringify(param1) === JSON.stringify(param2) ||
			Number.isNaN(param1) && Number.isNaN(param2);
	}*/
};

export default {
	install(Vue, options) {
		Object.entries(extensionMap).forEach(([key, val]) => Vue[key] = Vue.prototype[key] = val);
	}
};
